package quartz.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Component
@Slf4j
public class SpringUtil implements ApplicationContextAware, ResourceLoaderAware {
  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    if (SpringUtil.getApplicationContext() == null) {
      synchronized (SpringUtil.class) {
        if (SpringUtil.getApplicationContext() == null) {
          SpringUtil.applicationContext = applicationContext;
        }

      }
    }
  }

  private static ApplicationContext getApplicationContext() {
    return applicationContext;
  }

  public static Object getBean(String name) {
    return getApplicationContext().getBean(name);
  }

  public static <T> T getBean(Class<T> clazz) {
    return getApplicationContext().getBean(clazz);
  }

  public static <T> T getBean(String name, Class<T> clazz) {
    return getApplicationContext().getBean(name, clazz);
  }

  private static void scanAllClasses() {
    allClasses = new ArrayList<>();
    ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
    MetadataReaderFactory metaReader = new CachingMetadataReaderFactory(resourceLoader);
    Resource[] resources;
    String locationPattern = "classpath*:com/sspr/**/*.class";
    try {
      resources = resolver.getResources(locationPattern);
      for (Resource r : resources) {
        MetadataReader reader = metaReader.getMetadataReader(r);
        allClasses.add(Class.forName(reader.getClassMetadata().getClassName()));
      }
    } catch (IOException | ClassNotFoundException e) {
      log.warn(MessageFormat.format("Cannot scan classes in {0}", locationPattern), e);
    }
  }

  public static <T> List getSubClasses(final Class<T> clazz) {
    if (allClasses == null) {
      scanAllClasses();
    }
    return allClasses.stream().filter(currentClass -> currentClass.getSuperclass().equals(clazz)
            || Arrays.stream(currentClass.getInterfaces()).anyMatch(it -> it.equals(clazz))).collect(Collectors.toList());
  }

  @Override
  public void setResourceLoader(ResourceLoader resourceLoader) {
    SpringUtil.resourceLoader = resourceLoader;
  }

  private static ApplicationContext applicationContext = null;
  private static ResourceLoader resourceLoader = null;
  private static List<Class> allClasses = null;
}
